﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace SharedLib.XmlDataModel
{
    /// <summary>
    /// Basic implementation of interface "IXmlDataModel"
    /// </summary>
    /// <seealso cref="IXmlDataModel"/>
    public abstract class XmlDataModel:IXmlDataModel
    {
        #region Constructors
        protected XmlDataModel()
        {
        }

        protected XmlDataModel(XmlNode node)
        {
            if (node == null)
            {
                throw new ArgumentNullException("node");
            }
            LoadXmlNode(node);
        }
        #endregion

        #region Public Static Functions
        /// <summary>
        /// Get Xml root node name by reflection.(Slow)
        /// </summary>
        /// <param name="type">Class Type</param>
        /// <returns>Xml root node name</returns>
        private string GetRootNodeName(Type type)
        {
            if(type == null)return null;

            XmlRootAttribute[] attrList = type.GetCustomAttributes(typeof(XmlRootAttribute), false).Cast<XmlRootAttribute>().ToArray();
            if (attrList != null && attrList.Length > 0)
            {
                return attrList[0].ElementName;
            }
            else
            {
                return type.Name;
            }
        }
        #endregion

        #region IXmlDataModel Members

        /// <summary>
        /// Get Xml root node name (run time)
        /// </summary>
        public virtual string RootNodeName
        {
            get { return GetRootNodeName(this.GetType()); }
        }



        /// <summary>
        /// Export content to an Xml node
        /// <para>Note: the exported node will be attached to the given Xml Document</para>
        /// </summary>
        /// <param name="doc">Xml document of the exporting node</param>
        /// <returns>Exported node</returns>
        public XmlElement ToXmlNode(XmlDocument doc)
        {
            XmlElement generatedNode = GenerateXmlNode(doc);
            //Find Root Node
            if (generatedNode.Name != RootNodeName)
            {
                generatedNode = (XmlElement)generatedNode.SelectSingleNode(RootNodeName);
            }
            return generatedNode;
        }

        /// <summary>
        /// Export content to an Xml node
        /// <para>Note: the exported node will be an unattached Xml element</para>
        /// </summary>
        /// <returns>Exported node</returns>
        public XmlElement ToXmlNode()
        {
            return ToXmlNode(new XmlDocument());
        }

        /// <summary>
        /// Export content to a whole Xml document
        /// </summary>
        /// <returns>Exported Xml document</returns>
        public XmlDocument ToXmlDocument()
        {
            XmlDocument doc = new XmlDocument();
            doc.AppendChild(ToXmlNode(doc));
            return doc;
        }

        /// <summary>
        /// Export content to Xml text
        /// </summary>
        /// <param name="doc">Xml document to be used</param>
        /// <returns>Exported Xml text</returns>
        public string ToXmlText(XmlDocument doc)
        {
            return ToXmlNode(doc).OuterXml;
        }

        /// <summary>
        /// Export content to Xml text
        /// </summary>
        /// <returns>Exported Xml text</returns>
        public string ToXmlText()
        {
            return ToXmlText(new XmlDocument());
        }

        /// <summary>
        /// Load content from Xml text
        /// </summary>
        /// <param name="xmlText">Xml text to be imported</param>
        public void LoadXml(string xmlText)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xmlText);
            LoadXmlNode(doc);
        }

        /// <summary>
        /// Load content from Xml node
        /// </summary>
        /// <param name="node">Xml node to be imported</param>
        public void LoadXmlNode(XmlNode node)
        {
            if (node == null) throw new ArgumentNullException("node");

            XmlNode nodeToParse = node;
            if (node.Name != RootNodeName)
            {
                nodeToParse = node.SelectSingleNode(RootNodeName);
            }
            if (nodeToParse != null && nodeToParse is XmlElement && nodeToParse.Name == RootNodeName)
            {
                ParseRootNode((XmlElement)nodeToParse);
            }
            else
            {
                throw new ArgumentException("Element Name '" + RootNodeName + "' not found!", "node");
            }
        }

        #endregion

        #region To be overriden

        /// <summary>
        /// Parse root node when importing Xml
        /// </summary>
        /// <param name="rootNode">Importing root node</param>
        protected virtual void ParseRootNode(XmlElement rootNode)
        {
        }

        /// <summary>
        /// Generate Xml detail when exporting Xml
        /// </summary>
        /// <param name="doc">Xml document of the exporting node</param>
        /// <returns>Generated Xml element</returns>
        protected virtual XmlElement GenerateXmlNode(XmlDocument doc)
        {
            return doc.CreateElement(RootNodeName);
        }


        #endregion
    }
}
